/*
   american fuzzy lop - hashing function
   -------------------------------------

   Original AFL code written by Michal Zalewski <lcamtuf@google.com>

   Windows fork written and maintained by Ivan Fratric <ifratric@google.com>

   Copyright 2016 Google Inc. All Rights Reserved.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

   This file has been modified from the original to suit the purposes of this
   project.
*/

/* 
   The hash32() function is a variant of MurmurHash3, a good
   non-cryptosafe hashing function developed by Austin Appleby.

   For simplicity, this variant does *NOT* accept buffer lengths
   that are not divisible by 8 bytes. The 32-bit version is otherwise
   similar to the original; the 64-bit one is a custom hack with
   mostly-unproven properties.

   Austin's original code is public domain.

*/

#ifndef _HAVE_HASH_H
#define _HAVE_HASH_H

#include "winafl_types.h"

#if defined(_M_X64) || defined(__x86_64__)

#define ROL64(_x, _r)  ((((u64)(_x)) << (_r)) | (((u64)(_x)) >> (64 - (_r))))

static inline u32 hash32(const void* key, u32 len, u32 seed) {

  const u64* data = (u64*)key;
  u64 h1 = seed ^ len;

  len >>= 3;

  while (len--) {

    u64 k1 = *data++;

    k1 *= 0x87c37b91114253d5ULL;
    k1  = ROL64(k1, 31);
    k1 *= 0x4cf5ad432745937fULL;

    h1 ^= k1;
    h1  = ROL64(h1, 27);
    h1  = h1 * 5 + 0x52dce729;

  }

  h1 ^= h1 >> 33;
  h1 *= 0xff51afd7ed558ccdULL;
  h1 ^= h1 >> 33;
  h1 *= 0xc4ceb9fe1a85ec53ULL;
  h1 ^= h1 >> 33;

  return h1;

}

static inline u32 hash32_with_ignore(const void* key, const void * ignore, u32 len, u32 seed) {

	const u64* data = (u64*)key;
	const u64* ignore_data = (u64*)ignore;
	u64 h1 = seed ^ len;

	len >>= 3;

	while (len--) {

		u64 k1 = *data++;
		u64 i1 = *ignore_data++;
		k1 = k1 & i1; //mask off any ignore'd bytes

		k1 *= 0x87c37b91114253d5ULL;
		k1 = ROL64(k1, 31);
		k1 *= 0x4cf5ad432745937fULL;

		h1 ^= k1;
		h1 = ROL64(h1, 27);
		h1 = h1 * 5 + 0x52dce729;

	}

	h1 ^= h1 >> 33;
	h1 *= 0xff51afd7ed558ccdULL;
	h1 ^= h1 >> 33;
	h1 *= 0xc4ceb9fe1a85ec53ULL;
	h1 ^= h1 >> 33;

	return h1;

}

#else 

#define ROL32(_x, _r)  ((((u32)(_x)) << (_r)) | (((u32)(_x)) >> (32 - (_r))))

static inline u32 hash32(const void* key, u32 len, u32 seed) {

  const u32* data  = (u32*)key;
  u32 h1 = seed ^ len;

  len >>= 2;

  while (len--) {

    u32 k1 = *data++;

    k1 *= 0xcc9e2d51;
    k1  = ROL32(k1, 15);
    k1 *= 0x1b873593;

    h1 ^= k1;
    h1  = ROL32(h1, 13);
    h1  = h1 * 5 + 0xe6546b64;

  }

  h1 ^= h1 >> 16;
  h1 *= 0x85ebca6b;
  h1 ^= h1 >> 13;
  h1 *= 0xc2b2ae35;
  h1 ^= h1 >> 16;

  return h1;

}


static inline u32 hash32_with_ignore(const void* key, const void * ignore, u32 len, u32 seed) {

	const u32* data = (u32*)key;
	const u32* ignore_data = (u32*)ignore;
	const u8* ignore_byte = (u8*)ignore;

	u32 h1 = seed ^ len;

	len >>= 2;

	while (len--) {

		u32 k1 = *data++;
/*
		u32 i1 = *ignore_data++;
		k1 = k1 & i1; //mask off any ignore'd bytes
*/
		u8 i1;

		i1 = *ignore_byte;
		ignore_byte++;
		if (i1)
			k1 = k1 & 0xffffff00;

		i1 = *ignore_byte;
		ignore_byte++;
		if (i1)
			k1 = k1 & 0xffff00ff;

		i1 = *ignore_byte;
		ignore_byte++;
		if (i1)
			k1 = k1 & 0xff00ffff;

		i1 = *ignore_byte;
		ignore_byte++;
		if (i1)
			k1 = k1 & 0x00ffffff;



		k1 *= 0xcc9e2d51;
		k1 = ROL32(k1, 15);
		k1 *= 0x1b873593;

		h1 ^= k1;
		h1 = ROL32(h1, 13);
		h1 = h1 * 5 + 0xe6546b64;

	}

	h1 ^= h1 >> 16;
	h1 *= 0x85ebca6b;
	h1 ^= h1 >> 13;
	h1 *= 0xc2b2ae35;
	h1 ^= h1 >> 16;

	return h1;

}

#endif /* ^__x86_64__ */

#endif /* !_HAVE_HASH_H */
